﻿namespace StochasticSimulation.ChemicalReaction.PriorityQueue

open StochasticSimulation.ChemicalReaction.ChemicalReaction
open System
open System.Diagnostics

type NextTime =
    | Normal of double // next time
    | Infinite of double //  stored value for calculating next time once propensity returns to non-zero : next time is always PositiveInfinity

(* note : obviously this isn't a real priority queue. But it will 
do the job until I can figure out how to do one *)
type PriorityQueue( system, reactions : Reaction list, startTime ) =
    let reactionTimes = 
        let dict = new System.Collections.Generic.Dictionary<Reaction,NextTime>()
        let rng = new System.Random()
        //let fluct = log(1.0/ rng.NextDouble())
        let prop (r:Reaction) = r.Propensity system
        let next f p =
            match p with
                | 0.0  -> Infinite( startTime )
                | _    -> Normal(f/p)
        reactions 
            |> List.iter (fun r -> dict.Add(r, next (log(1.0/ rng.NextDouble())) (prop r ) )) 
            |> ignore
        dict    
    
    new (system, reactions : Reaction list)  =
        PriorityQueue(system, reactions, 0.0)

    
    member this.Update (updates : (Reaction*NextTime) list) =
        updates |> List.iter (fun u -> reactionTimes.[fst u] <- snd u) |> ignore

    member this.Next =
        let reactionTime nt =
            match nt with
                | Normal(t)    -> t
                | Infinite(t)  -> Double.PositiveInfinity
        let kv = reactionTimes |> Seq.minBy (fun rt -> reactionTime rt.Value )
        (kv.Key,kv.Value)

    member this.Times reactions =
        reactions |> List.map ( fun r -> reactionTimes.[r] )